/**
 * Jaxson ajax
 *
 * Copyright Remedy Interactive, licensed under Apache version 2 license:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0.html
 *
 * This notice must remain in all java source files.
 */

package org.jaxson.config;


import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jaxson.util.locale.LocaleUtil;

/**
 * Class containing javascript engine configuration parameters.  These parameters
 * are set as init-params in the web.xml for the servlet.
 * 
 * @author Joe Maisel
 */
public class JavascriptConfiguration extends HttpServlet
{
	private static final long serialVersionUID = 3681794939623650304L;

	private static JavascriptConfiguration me;
	private ServletContext ctx;

	// public static final String BASE_HTTP_PATH_KEY =
	// JavascriptConfiguration.class.getName () + ".http.path";
	// public static final String BASE_FILE_PATH_KEY =
	// JavascriptConfiguration.class.getName () + ".file.path";

	private static final Log lager = LogFactory.getLog( JavascriptConfiguration.class.getName() );
	
	/**
	 * enum containing javascript linker configuration.
	 * 
	 * @author joe
	 *
	 */
	public enum Config
	{
		JS_SOURCE_DIR(true), JS_FILE_EXTENSION(true), JS_FILE_SYSTEM_DIR(false), CLIENT_JS_EXTENTION(false),UNPACKAGE_JS(false), UNPACKAGE_ADMIN(false);

		private String value;
		private boolean required;

		private Config( boolean required )
		{
			this.required = required;
		}
		
		/**
		 * Returns the enum name in it's web.xml init-param format,
		 * ie JS_SOURCE_DIR becomes js.source.dir
		 */
		public String asParam()
		{
			return name().toLowerCase().replace( '_', '.' );
		}

		public String value()
		{
			return value;
		}
	};
	
	private Config config;
	
	public JavascriptConfiguration()
	{
		if( me != null )
			throw new IllegalStateException( "There can be only one!  This class should only be instantiated by the servlet container." );
	}

	/**
	 * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
	 */
	@Override
	public void init( ServletConfig config ) throws ServletException
	{
		lager.debug( "init called" );
		me = this;
		
		Config.UNPACKAGE_JS.value = "true";
		Config.CLIENT_JS_EXTENTION.value = ".cjs";
		for( Config c : Config.values() )
		{
			if( c.required )
			{
				if( config.getInitParameter( c.asParam() ) == null )
				{
					throw new IllegalArgumentException( "The required config parameter " + c.asParam()
							+ " wasn't supplied." );
				}
			}
			
			if( config.getInitParameter( c.asParam() ) != null )
				c.value = config.getInitParameter( c.asParam() );
		}

		for( Config c : Config.values() )
		{
			lager.debug( c.asParam() + "=" + c.value );
		}

		// set the file system dir
		Config.JS_FILE_SYSTEM_DIR.value = config.getServletContext().getRealPath( Config.JS_SOURCE_DIR.value() );
		
		try
		{
			ctx = config.getServletContext();
		
			if( Boolean.valueOf(Config.UNPACKAGE_JS.value()) )
				unpackageJavascript();
			
			if( Boolean.valueOf(Config.UNPACKAGE_ADMIN.value()) )
				unpackageAdmin();
			
			// force the properties to be loaded at startup
			ApplicationConfiguration.getInstance();
			
			// set the ServletContext in the LocalUtil
			LocaleUtil.setServletContext( ctx );
		}
		catch( IOException e )
		{
			e.printStackTrace();
			throw new ServletException( e );
		}
	}
	
	@Override
	public ServletContext getServletContext(){
		return ctx;
	}
	
	/**
	 * Unpackage the base javascript file included with the framework.
	 */
	private void unpackageJavascript() throws IOException
	{
		lager.debug( "unpackageJavascript called" );

    	String root = getServletContext().getRealPath( "/" );
    	String p = getServletContext().getRealPath( "WEB-INF/lib/jaxson-js.jar" );
    	File f = new File( p );

		FileInputStream fis = new FileInputStream( f );
		ZipInputStream zis = new ZipInputStream( fis );
		ZipEntry entry;

		while( (entry = zis.getNextEntry()) != null )
		{
			File f1 = new File( root + File.separator + Config.JS_SOURCE_DIR.value() + File.separator + entry.getName() );
			
//			if( f1.isFile() && f1.exists() )
//			{
//				lager.warn( "WARNING: Javascript class " + entry.getName().replace('/', '.').replaceAll( "\\.js", "" ) + " exists and will not be overwritten" );
//				continue;
//			}
			
			if( entry.isDirectory() )
			{
				f1.mkdirs();
				continue;
			}
			
			int size;
			byte[] buffer = new byte[(int) entry.getSize()];
			FileOutputStream fos = new FileOutputStream( f1 );
			BufferedOutputStream bos = new BufferedOutputStream( fos, buffer.length );

			while( (size = zis.read( buffer, 0, buffer.length )) != -1 )
			{
				bos.write( buffer, 0, size );
			}
			
			bos.flush();
			bos.close();
		}
	}
	
	private void unpackageAdmin() throws IOException
	{
		lager.debug( "unpackageAdmin called" );

    	String root = getServletContext().getRealPath( "/" );
    	String p = getServletContext().getRealPath( "WEB-INF/lib/jaxson-admin.jar" );
    	File f = new File( p );

		FileInputStream fis = new FileInputStream( f );
		ZipInputStream zis = new ZipInputStream( fis );
		ZipEntry entry;

		while( (entry = zis.getNextEntry()) != null )
		{
			File f1 = new File( root + File.separator + "admin" + File.separator + entry.getName() );
			
			if( f1.isFile() && f1.exists() )
			{
				lager.warn( "WARNING: admin screen " + entry.getName() + " exists and will not be overwritten" );
				continue;
			}
			
			if( entry.isDirectory() )
			{
				f1.mkdirs();
				continue;
			}
			
			int size;
			byte[] buffer = new byte[(int) entry.getSize()];
			FileOutputStream fos = new FileOutputStream( f1 );
			BufferedOutputStream bos = new BufferedOutputStream( fos, buffer.length );

			while( (size = zis.read( buffer, 0, buffer.length )) != -1 )
			{
				bos.write( buffer, 0, size );
			}
			
			bos.flush();
			bos.close();
		}
	}
	
	/**
	 * Get a <code>org.jaxson.config.JavascriptConfiguration.Config</code>
	 * configuration enum.
	 */
	public Config getConfiguration()
	{
		return config;
	}
	
	/**
	 * psuedo singleton accessor.  This class should never be instantiated
	 * directly, always accessed as a singleton.
	 */
	public static JavascriptConfiguration getInstance()
	{
		return me;
	}
	
	/**
	 * Disallow access.
	 * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	protected void service( HttpServletRequest request, HttpServletResponse response )
	{
		response.setStatus( HttpServletResponse.SC_FORBIDDEN );
	}
}
